package com.flow.framework.core.util;

import com.flow.framework.common.error.SystemErrorCode;
import com.flow.framework.common.exception.CheckedException;
import com.flow.framework.common.util.clazz.ClazzUtil;
import com.flow.framework.common.util.verify.VerifyUtil;
import com.flow.framework.core.system.initialization.ApplicationContextHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.SpringProxy;

import java.lang.reflect.Proxy;
import java.util.Collection;

/**
 * 代理工具类
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/1/23
 */
@Slf4j
public class ProxyUtil {

    /**
     * 从application context中获取被代理的bean，如果不存在则返回空，否则则返回被代理的bean，如果没有被代理的bean则返回未被代理的
     * 使用场景为如果某个bean被spring代理后，如果使用接口从容器总获取bean，则会返回两个对象，其中一个为被代理对象，另外一个则为未被代理对象
     *
     * @param clazz clazz
     * @param <T>   T
     * @return bean
     */
    public static <T> T getProxiedQuietly(Class<T> clazz) {
        if (null == clazz) {
            log.error("clazz is null.");
            throw new CheckedException(SystemErrorCode.PARAMS_ERROR);
        }
        Collection<T> beans = ApplicationContextHelper.getBeans(clazz);
        if (VerifyUtil.isEmpty(beans)) {
            return null;
        }
        for (T bean : beans) {
            boolean isProxy = Proxy.isProxyClass(bean.getClass())
                    || (bean instanceof SpringProxy && bean.getClass().getName().contains("$$"));
            if (isProxy) {
                return bean;
            }
        }
        return beans.iterator().next();
    }

    /**
     * 从application context中获取被代理的bean，如果不存在则抛出异常，否则则返回被代理的bean，如果没有被代理的bean则返回未被代理的
     * 使用场景为如果某个bean被spring代理后，如果使用接口从容器总获取bean，则会返回两个对象，其中一个为被代理对象，另外一个则为未被代理对象
     *
     * @param clazz clazz
     * @param <T>   T
     * @return bean
     */
    public static <T> T getProxied(Class<T> clazz) {
        T proxied = getProxiedQuietly(clazz);
        if (null == proxied) {
            log.error("can't find bean. clazz name : {}", ClazzUtil.getShortByClazzName(clazz.getName()));
            throw new CheckedException(SystemErrorCode.OBJECT_NOT_FOUND_ERROR);
        }
        return proxied;
    }
}
